/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.correction;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.graphics.Image;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.ui.ISharedImages;
import org.eclipse.jdt.core.CorrectionEngine;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.text.java.IInvocationContext;
import org.eclipse.jdt.ui.text.java.IProblemLocation;
import org.eclipse.jdt.ui.text.java.correction.ASTRewriteCorrectionProposal;
import org.eclipse.jdt.ui.text.java.correction.ICommandAccess;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
/**
*
*/
public class SuppressWarningsSubProcessor {
private static final String ADD_SUPPRESSWARNINGS_ID= "org.eclipse.jdt.ui.correction.addSuppressWarnings"; //$NON-NLS-1$
public static final boolean hasSuppressWarningsProposal(IJavaProject javaProject, int problemId) {
if (CorrectionEngine.getWarningToken(problemId) != null && JavaModelUtil.is50OrHigher(javaProject)) {
String optionId= JavaCore.getOptionForConfigurableSeverity(problemId);
if (optionId != null) {
String optionValue= javaProject.getOption(optionId, true);
return JavaCore.WARNING.equals(optionValue) ||
(JavaCore.ERROR.equals(optionValue) && JavaCore.ENABLED.equals(javaProject.getOption(JavaCore.COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS, true)));
}
}
return false;
}
public static void addSuppressWarningsProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) {
if (problem.isError() && ! JavaCore.ENABLED.equals(context.getCompilationUnit().getJavaProject().getOption(JavaCore.COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS, true))) {
return;
}
if (JavaCore.DISABLED.equals(context.getCompilationUnit().getJavaProject().getOption(JavaCore.COMPILER_PB_SUPPRESS_WARNINGS, true))) {
return;
}
String warningToken= CorrectionEngine.getWarningToken(problem.getProblemId());
if (warningToken == null) {
return;
}
for (Iterator<ICommandAccess> iter= proposals.iterator(); iter.hasNext();) {
Object element= iter.next();
if (element instanceof SuppressWarningsProposal && warningToken.equals(((SuppressWarningsProposal) element).getWarningToken())) {
return; // only one at a time
}
}
ASTNode node= problem.getCoveringNode(context.getASTRoot());
if (node == null) {
return;
}
ASTNode target= node;
int relevance= IProposalRelevance.ADD_SUPPRESSWARNINGS;
do {
relevance= addSuppressWarningsProposalIfPossible(context.getCompilationUnit(), target, warningToken, relevance, proposals);
if (relevance == 0)
return;
target= target.getParent();
} while (target != null);
ASTNode importStatement= ASTNodes.getParent(node, ImportDeclaration.class);
if (importStatement != null && !context.getASTRoot().types().isEmpty()) {
target= (ASTNode) context.getASTRoot().types().get(0);
if (target != null) {
addSuppressWarningsProposalIfPossible(context.getCompilationUnit(), target, warningToken, IProposalRelevance.ADD_SUPPRESSWARNINGS, proposals);
}
}
}
private static String getFirstFragmentName(List<VariableDeclarationFragment> fragments) {
if (fragments.size() > 0) {
return fragments.get(0).getName().getIdentifier();
}
return new String();
}
private static class SuppressWarningsProposal extends ASTRewriteCorrectionProposal {
private final String fWarningToken;
private final ASTNode fNode;
private final ChildListPropertyDescriptor fProperty;
public SuppressWarningsProposal(String warningToken, String label, ICompilationUnit cu, ASTNode node, ChildListPropertyDescriptor property, int relevance) {
super(label, cu, null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_OBJS_JAVADOCTAG));
fWarningToken= warningToken;
fNode= node;
fProperty= property;
setCommandId(ADD_SUPPRESSWARNINGS_ID);
}
/**
* @return Returns the warningToken.
*/
public String getWarningToken() {
return fWarningToken;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.ui.text.correction.ASTRewriteCorrectionProposal#getRewrite()
*/
@Override
protected ASTRewrite getRewrite() throws CoreException {
AST ast= fNode.getAST();
ASTRewrite rewrite= ASTRewrite.create(ast);
StringLiteral newStringLiteral= ast.newStringLiteral();
newStringLiteral.setLiteralValue(fWarningToken);
Annotation existing= findExistingAnnotation(ASTNodes.getChildListProperty(fNode, fProperty));
if (existing == null) {
ListRewrite listRewrite= rewrite.getListRewrite(fNode, fProperty);
SingleMemberAnnotation newAnnot= ast.newSingleMemberAnnotation();
String importString= createImportRewrite((CompilationUnit) fNode.getRoot()).addImport("java.lang.SuppressWarnings"); //$NON-NLS-1$
newAnnot.setTypeName(ast.newName(importString));
newAnnot.setValue(newStringLiteral);
listRewrite.insertFirst(newAnnot, null);
} else if (existing instanceof SingleMemberAnnotation) {
SingleMemberAnnotation annotation= (SingleMemberAnnotation) existing;
Expression value= annotation.getValue();
if (!addSuppressArgument(rewrite, value, newStringLiteral)) {
rewrite.set(existing, SingleMemberAnnotation.VALUE_PROPERTY, newStringLiteral, null);
}
} else if (existing instanceof NormalAnnotation) {
NormalAnnotation annotation= (NormalAnnotation) existing;
Expression value= findValue(annotation.values());
if (!addSuppressArgument(rewrite, value, newStringLiteral)) {
ListRewrite listRewrite= rewrite.getListRewrite(annotation, NormalAnnotation.VALUES_PROPERTY);
MemberValuePair pair= ast.newMemberValuePair();
pair.setName(ast.newSimpleName("value")); //$NON-NLS-1$
pair.setValue(newStringLiteral);
listRewrite.insertFirst(pair, null);
}
}
return rewrite;
}
private static boolean addSuppressArgument(ASTRewrite rewrite, Expression value, StringLiteral newStringLiteral) {
if (value instanceof ArrayInitializer) {
ListRewrite listRewrite= rewrite.getListRewrite(value, ArrayInitializer.EXPRESSIONS_PROPERTY);
listRewrite.insertLast(newStringLiteral, null);
} else if (value instanceof StringLiteral) {
ArrayInitializer newArr= rewrite.getAST().newArrayInitializer();
newArr.expressions().add(rewrite.createMoveTarget(value));
newArr.expressions().add(newStringLiteral);
rewrite.replace(value, newArr, null);
} else {
return false;
}
return true;
}
private static Expression findValue(List<MemberValuePair> keyValues) {
for (int i= 0, len= keyValues.size(); i < len; i++) {
MemberValuePair curr= keyValues.get(i);
if ("value".equals(curr.getName().getIdentifier())) { //$NON-NLS-1$
return curr.getValue();
}
}
return null;
}
private static Annotation findExistingAnnotation(List<? extends ASTNode> modifiers) {
for (int i= 0, len= modifiers.size(); i < len; i++) {
Object curr= modifiers.get(i);
if (curr instanceof NormalAnnotation || curr instanceof SingleMemberAnnotation) {
Annotation annotation= (Annotation) curr;
ITypeBinding typeBinding= annotation.resolveTypeBinding();
if (typeBinding != null) {
if ("java.lang.SuppressWarnings".equals(typeBinding.getQualifiedName())) { //$NON-NLS-1$
return annotation;
}
} else {
String fullyQualifiedName= annotation.getTypeName().getFullyQualifiedName();
if ("SuppressWarnings".equals(fullyQualifiedName) || "java.lang.SuppressWarnings".equals(fullyQualifiedName)) { //$NON-NLS-1$ //$NON-NLS-2$
return annotation;
}
}
}
}
return null;
}
}
/**
* Adds a SuppressWarnings proposal if possible and returns whether parent nodes should be processed or not (and with what relevance).
*
* @param cu the compilation unit
* @param node the node on which to add a SuppressWarning token
* @param warningToken the warning token to add
* @param relevance the proposal's relevance
* @param proposals collector to which the proposal should be added
* @return <code>0</code> if no further proposals should be added to parent nodes, or the relevance of the next proposal
*
* @since 3.6
*/
private static int addSuppressWarningsProposalIfPossible(ICompilationUnit cu, ASTNode node, String warningToken, int relevance, Collection<ICommandAccess> proposals) {
ChildListPropertyDescriptor property;
String name;
boolean isLocalVariable= false;
switch (node.getNodeType()) {
case ASTNode.SINGLE_VARIABLE_DECLARATION:
property= SingleVariableDeclaration.MODIFIERS2_PROPERTY;
name= ((SingleVariableDeclaration) node).getName().getIdentifier();
isLocalVariable= true;
break;
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
property= VariableDeclarationStatement.MODIFIERS2_PROPERTY;
name= getFirstFragmentName(((VariableDeclarationStatement) node).fragments());
isLocalVariable= true;
break;
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
property= VariableDeclarationExpression.MODIFIERS2_PROPERTY;
name= getFirstFragmentName(((VariableDeclarationExpression) node).fragments());
isLocalVariable= true;
break;
case ASTNode.TYPE_DECLARATION:
property= TypeDeclaration.MODIFIERS2_PROPERTY;
name= ((TypeDeclaration) node).getName().getIdentifier();
break;
case ASTNode.ANNOTATION_TYPE_DECLARATION:
property= AnnotationTypeDeclaration.MODIFIERS2_PROPERTY;
name= ((AnnotationTypeDeclaration) node).getName().getIdentifier();
break;
case ASTNode.ENUM_DECLARATION:
property= EnumDeclaration.MODIFIERS2_PROPERTY;
name= ((EnumDeclaration) node).getName().getIdentifier();
break;
case ASTNode.FIELD_DECLARATION:
property= FieldDeclaration.MODIFIERS2_PROPERTY;
name= getFirstFragmentName(((FieldDeclaration) node).fragments());
break;
// case ASTNode.INITIALIZER: not used, because Initializer cannot have annotations
case ASTNode.METHOD_DECLARATION:
property= MethodDeclaration.MODIFIERS2_PROPERTY;
name= ((MethodDeclaration) node).getName().getIdentifier() + "()"; //$NON-NLS-1$
break;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
property= AnnotationTypeMemberDeclaration.MODIFIERS2_PROPERTY;
name= ((AnnotationTypeMemberDeclaration) node).getName().getIdentifier() + "()"; //$NON-NLS-1$
break;
case ASTNode.ENUM_CONSTANT_DECLARATION:
property= EnumConstantDeclaration.MODIFIERS2_PROPERTY;
name= ((EnumConstantDeclaration) node).getName().getIdentifier();
break;
default:
return relevance;
}
String label= Messages.format(CorrectionMessages.SuppressWarningsSubProcessor_suppress_warnings_label, new String[] { warningToken, BasicElementLabels.getJavaElementName(name) });
ASTRewriteCorrectionProposal proposal= new SuppressWarningsProposal(warningToken, label, cu, node, property, relevance);
proposals.add(proposal);
return isLocalVariable ? relevance - 1 : 0;
}
/**
* Adds a proposal to correct the name of the SuppressWarning annotation
* @param context the context
* @param problem the problem
* @param proposals the resulting proposals
*/
public static void addUnknownSuppressWarningProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) {
ASTNode coveringNode= context.getCoveringNode();
if (!(coveringNode instanceof StringLiteral))
return;
AST ast= coveringNode.getAST();
StringLiteral literal= (StringLiteral) coveringNode;
String literalValue= literal.getLiteralValue();
String[] allWarningTokens= CorrectionEngine.getAllWarningTokens();
for (int i= 0; i < allWarningTokens.length; i++) {
String curr= allWarningTokens[i];
if (NameMatcher.isSimilarName(literalValue, curr)) {
StringLiteral newLiteral= ast.newStringLiteral();
newLiteral.setLiteralValue(curr);
ASTRewrite rewrite= ASTRewrite.create(ast);
rewrite.replace(literal, newLiteral, null);
String label= Messages.format(CorrectionMessages.SuppressWarningsSubProcessor_fix_suppress_token_label, new String[] { curr });
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.FIX_SUPPRESS_TOKEN, image);
proposals.add(proposal);
}
}
addRemoveUnusedSuppressWarningProposals(context, problem, proposals);
}
public static void addRemoveUnusedSuppressWarningProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) {
ASTNode coveringNode= problem.getCoveringNode(context.getASTRoot());
if (!(coveringNode instanceof StringLiteral))
return;
StringLiteral literal= (StringLiteral) coveringNode;
if (coveringNode.getParent() instanceof MemberValuePair) {
coveringNode= coveringNode.getParent();
}
ASTNode parent= coveringNode.getParent();
ASTRewrite rewrite= ASTRewrite.create(coveringNode.getAST());
if (parent instanceof SingleMemberAnnotation) {
rewrite.remove(parent, null);
} else if (parent instanceof NormalAnnotation) {
NormalAnnotation annot= (NormalAnnotation) parent;
if (annot.values().size() == 1) {
rewrite.remove(annot, null);
} else {
rewrite.remove(coveringNode, null);
}
} else if (parent instanceof ArrayInitializer) {
rewrite.remove(coveringNode, null);
} else {
return;
}
String label= Messages.format(CorrectionMessages.SuppressWarningsSubProcessor_remove_annotation_label, literal.getLiteralValue());
Image image= JavaPlugin.getDefault().getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE);
ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.REMOVE_ANNOTATION, image);
proposals.add(proposal);
}
}